#include "xmlrpc.h"
#include "parser.h"
#include "xmlrpctypes.h"
#include "log.h"

using namespace XmlRpc;
using namespace Xml;

EXPORT_C CResponseContentHandler::~CResponseContentHandler()
	{
	if (iState)
		delete iState;
	}

EXPORT_C void CResponseContentHandler::OnStartDocumentL(const RDocumentParameters& /*aDocParam*/, TInt /*aErrorCode*/)
	{
	iState = new(ELeave) CInitState(this, NULL, iResult);
	LOGF_1("OnStartDocument iState(%x)", iState);
	}

EXPORT_C void CResponseContentHandler::OnEndDocumentL(TInt /*aErrorCode*/)
	{
	LOGF_1("OnEndDocument iState(%x)", iState);
	delete iState;
	iState = NULL;
	}

EXPORT_C void CResponseContentHandler::OnStartElementL(const RTagInfo& aElement, const RAttributeArray& /*aAttributes*/, TInt /*aErrorCode*/)
	{
	LOGF_2("OnStartElement %S iState(%x)", &aElement.LocalName().DesC(), iState);
	iState->InputL(aElement);
	}

EXPORT_C void CResponseContentHandler::OnEndElementL(const RTagInfo& aElement, TInt /*aErrorCode*/)
	{
	LOGF_1("OnEndElement %S", &aElement.LocalName().DesC());
	iState->InputL(aElement, ETrue);
	}

EXPORT_C void CResponseContentHandler::OnContentL(const TDesC8& aBytes, TInt /*aErrorCode*/)
	{
	iState->InputL(aBytes);
	}

EXPORT_C void CResponseContentHandler::OnError(TInt /*aErrorCode*/)
	{
	}

void CInitState::InputL(const RTagInfo& aElement, TBool /*aEndTag*/)
	{
	if (aElement.LocalName().DesC().Compare(KMethodResponseTag()) == 0)
		{
		CContentHandlerState* next = new(ELeave)CMethodResponseState(ContentHandler(), this, iResult);
		EnterState(next);
		}
	else
		{
		User::Leave(KErrArgument);
		}	
	}

void CMethodResponseState::InputL(const RTagInfo& aElement, TBool aEndTag)
	{
	if (aElement.LocalName().DesC().Compare(KParamsTag()) == 0)
		{
		if (!aEndTag)
			{
			CContentHandlerState* next = new(ELeave)CParamsState(ContentHandler(), this, iResult);
			EnterState(next);
			}
		}
	else if (aElement.LocalName().DesC().Compare(KFaultTag()) == 0)
		{
		if (!aEndTag)
			{
			CContentHandlerState* next = new(ELeave)CFaultState(ContentHandler(), this, iResult);
			EnterState(next);
			}
		}
	else if (aElement.LocalName().DesC().Compare(KMethodResponseTag()) == 0 && aEndTag)
		{
		Finish();
		}
	else
		{
		User::Leave(KErrArgument);
		}	

	}

void CFaultState::InputL(const RTagInfo& aElement, TBool aEndTag)
	{
	if (aElement.LocalName().DesC().Compare(KValueTag()) == 0)
		{
		if (!aEndTag)
			{
			CContentHandlerState* next = new(ELeave)CValueState(ContentHandler(), this, iResult);
			EnterState(next);
			}
		}
	else if (aElement.LocalName().DesC().Compare(KFaultTag()) == 0 && aEndTag)
		{
		Finish();
		}
	else
		{
		User::Leave(KErrArgument);
		}	
	}

void CFaultState::FoundValueL(CValue* aValue)
	{
	_LIT8(KFaultCodeKey, "faultCode");
	_LIT8(KFaultStringKey, "faultString");

	CleanupStack::PushL(aValue);
	if (aValue->TypeId() != XmlRpc::EStruct)
		{
		User::Leave(KErrArgument);
		}

	CStruct* structp = static_cast<CStruct*>(aValue);
	CValue* v = structp->GetL(KFaultCodeKey);
	if (v->TypeId() != XmlRpc::EInt)
		{
		User::Leave(KErrArgument);
		}
	CInt* intp = static_cast<CInt*>(v);
	iResult.SetErrorCode(intp->Get());

	v = structp->RemoveL(KFaultStringKey);
	if (v->TypeId() != XmlRpc::EString)
		{
		delete v;
		User::Leave(KErrArgument);
		}
	iResult.SetResult(v);
	CleanupStack::PopAndDestroy(aValue);
	}

void CParamsState::InputL(const RTagInfo& aElement, TBool aEndTag)
	{
	if (aElement.LocalName().DesC().Compare(KParamTag()) == 0)
		{
		if (!aEndTag)
			{
			CContentHandlerState* next = new(ELeave)CParamState(ContentHandler(), this, iResult);
			EnterState(next);
			}
		}
	else if (aElement.LocalName().DesC().Compare(KParamsTag()) == 0 && aEndTag)
		{
		Finish();
		}
	else
		{
		User::Leave(KErrArgument);
		}	
	}

void CParamsState::FoundValueL(CValue* aValue)
	{
	iResult.SetErrorCode(KErrNone);
	iResult.SetResult(aValue); // ownership transfered to result
	}

void CParamState::InputL(const Xml::RTagInfo& aElement, TBool aEndTag)
	{
	if (aElement.LocalName().DesC().Compare(KValueTag()) == 0)
		{
		if (!aEndTag)
			{
			CContentHandlerState* next = new(ELeave)CValueState(ContentHandler(), this, iResult);
			EnterState(next);
			}
		}
	else if (aElement.LocalName().DesC().Compare(KParamTag()) == 0 && aEndTag)
		{
		Finish();
		}
	else
		{
		User::Leave(KErrArgument);
		}		
	}

void CParamState::FoundValueL(CValue* aValue)
	{
	iParentState->FoundValueL(aValue);
	}

void CValueState::InputL(const RTagInfo& aElement, TBool aEndTag)
	{
	if (!aEndTag)
		{
		CContentHandlerState* next = NULL;
		if ((aElement.LocalName().DesC().Compare(KIntTag1()) == 0)
			|| (aElement.LocalName().DesC().Compare(KIntTag2()) == 0))
			{
			next = new(ELeave)CIntState(ContentHandler(), this, iResult);
			}
		else if (aElement.LocalName().DesC().Compare(KBooleanTag()) == 0)
			{
			next = new(ELeave)CBooleanState(ContentHandler(), this, iResult);
			}
		else if (aElement.LocalName().DesC().Compare(KDoubleTag()) == 0)
			{
			next = new(ELeave)CDoubleState(ContentHandler(), this, iResult);
			}
		else if (aElement.LocalName().DesC().Compare(KDateTimeTag()) == 0)
			{
			next = new(ELeave)CDateTimeState(ContentHandler(), this, iResult);
			}
		else if (aElement.LocalName().DesC().Compare(KStringTag()) == 0)
			{
			next = new(ELeave)CStringState(ContentHandler(), this, iResult);
			}
		else if (aElement.LocalName().DesC().Compare(KBase64Tag()) == 0)
			{
			next = new(ELeave)CBase64State(ContentHandler(), this, iResult);
			}
		else if (aElement.LocalName().DesC().Compare(KStructTag()) == 0)
			{
			next = new(ELeave)CStructState(ContentHandler(), this, iResult);
			}
		else if (aElement.LocalName().DesC().Compare(KArrayTag()) == 0)
			{
			next = new(ELeave)CArrayState(ContentHandler(), this, iResult);
			}
		else
			{
			User::Leave(KErrArgument);
			}
		EnterState(next);
		}
	else if (aElement.LocalName().DesC().Compare(KValueTag()) == 0 && aEndTag)
		{
		Finish();
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

CValueState::~CValueState()
	{
	ASSERT (iValue == NULL);
	iBuf.Close();
	}

void CValueState::InputL(const TDesC8& aBytes)
	{
	TInt len = aBytes.Length();
	if (iBuf.Length() + len > iBuf.MaxLength())
		iBuf.ReAllocL(iBuf.MaxLength() + (KGrowIncrement > len ? KGrowIncrement : len));
	iBuf.Append(aBytes);
	}

void CValueState::FoundValueL(CValue* aValue)
	{
	iParentState->FoundValueL(aValue);
	}

void CIntState::InputL(const RTagInfo& aElement, TBool aEndTag)
	{
	if ((aElement.LocalName().DesC().Compare(KIntTag1()) == 0 || aElement.LocalName().DesC().Compare(KIntTag2()) == 0) && aEndTag)
		{
		TInt32 i;
		TLex8 lex(iBuf);
		User::LeaveIfError(lex.Val(i));
		
		ASSERT(iValue == NULL);
		iValue = CInt::NewL(i);

		iParentState->FoundValueL(iValue);
		iValue = NULL; // ownership passed up

		Finish();
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}


void CBooleanState::InputL(const RTagInfo& aElement, TBool aEndTag)
	{
	if (aElement.LocalName().DesC().Compare(KBooleanTag()) == 0 && aEndTag)
		{
		TInt32 i;
		TLex8 lex(iBuf);
		User::LeaveIfError(lex.Val(i));
		if (i != 0 || i != 1)
			User::Leave(KErrArgument);
		
		ASSERT(iValue == NULL);
		iValue = CBoolean::NewL(i);

		iParentState->FoundValueL(iValue);
		iValue = NULL; // ownership passed up

		Finish();
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}


void CDoubleState::InputL(const RTagInfo& aElement, TBool aEndTag)
	{
	if (aElement.LocalName().DesC().Compare(KDoubleTag()) == 0 && aEndTag)
		{
		TReal i;
		TLex8 lex(iBuf);
		User::LeaveIfError(lex.Val(i));
		
		ASSERT(iValue == NULL);
		iValue = CDouble::NewL(i);

		iParentState->FoundValueL(iValue);
		iValue = NULL; // ownership passed up

		Finish();
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}


void CDateTimeState::InputL(const RTagInfo& aElement, TBool aEndTag)
	{
	if (aElement.LocalName().DesC().Compare(KDateTimeTag()) == 0 && aEndTag)
		{
		TUint y,M,d,h,m,s;
		if (iBuf.Length() < 17)
			User::Leave(KErrArgument);
		TPtrC8 yp = iBuf.Mid(0, 4);
		TPtrC8 Mp = iBuf.Mid(4, 2);
		TPtrC8 dp = iBuf.Mid(6, 2);
		TPtrC8 hp = iBuf.Mid(9, 2);
		TPtrC8 mp = iBuf.Mid(12, 2);
		TPtrC8 sp = iBuf.Mid(15, 2);
		TLex8 yl(yp), Ml(Mp), dl(dp), hl(hp), ml(mp), sl(sp);
		User::LeaveIfError(yl.Val(y));
		User::LeaveIfError(Ml.Val(M));
		User::LeaveIfError(dl.Val(d));
		User::LeaveIfError(hl.Val(h));
		User::LeaveIfError(ml.Val(m));
		User::LeaveIfError(sl.Val(s));

		
		ASSERT(iValue == NULL);
		TMonth month  = static_cast<TMonth>(M-1);
		iValue = CDateTime::NewL(TDateTime(y, month, d-1, h, m, s, 0));

		iParentState->FoundValueL(iValue);
		iValue = NULL; // ownership passed up

		Finish();
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

void CStringState::InputL(const RTagInfo& aElement, TBool aEndTag)
	{
	if (aElement.LocalName().DesC().Compare(KStringTag()) == 0 && aEndTag)
		{
		RBuf buf;
		XmlEncoding::DecodeL(iBuf, buf);
		CleanupClosePushL(buf);
		
		iValue = CString::NewL(buf);

		CleanupStack::PopAndDestroy(&buf);

		iParentState->FoundValueL(iValue);
		iValue = NULL; // ownership passed up

		Finish();
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

void CBase64State::InputL(const RTagInfo& aElement, TBool aEndTag)
	{
	if (aElement.LocalName().DesC().Compare(KBase64Tag()) == 0 && aEndTag)
		{
		iValue = CBase64::NewFromEncodedL(iBuf);
		iParentState->FoundValueL(iValue);
		iValue = NULL; // ownership passed up

		Finish();
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

void CStructState::InputL(const RTagInfo& aElement, TBool aEndTag)
	{
	if (aElement.LocalName().DesC().Compare(KMemberTag()) == 0)
		{
		if (!aEndTag)
			{
			CContentHandlerState* next = new(ELeave)CMemberState(ContentHandler(), this, iResult);
			EnterState(next);
			}
		}
	else if (aElement.LocalName().DesC().Compare(KStructTag()) == 0 && aEndTag)
		{
		iParentState->FoundValueL(iValue);
		iValue = NULL; // ownership passed up

		Finish();
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

void CStructState::InputL(const TDesC8& /*aBytes*/)
	{
	}

void CStructState::FoundValueL(CValue* /*aValue*/)
	{
	ASSERT(0); // impossible state
	}

void CStructState::FoundMemberL(const TDesC8& aKey, CValue* aValue)
	{
	CStruct* stp = GetStructL();
	stp->SetL(aKey, aValue);
	}

CStruct* CStructState::GetStructL()
	{
	if (iValue == NULL)
		iValue = CStruct::NewL();
	return static_cast<CStruct*>(iValue);
	}

CMemberState::~CMemberState()
	{
	iName.Close();
	}

void CMemberState::InputL(const Xml::RTagInfo& aElement, TBool aEndTag)
	{
	if (aElement.LocalName().DesC().Compare(KMemberTag) == 0 && aEndTag)
		{
		if (!iName.Length() || iValue == NULL)
			{
			User::Leave(KErrArgument);
			}
		CStructState* st = static_cast<CStructState*>(iParentState);
		st->FoundMemberL(iName, iValue);
		iName.Close();
		iValue = NULL;

		Finish();
		}
	else if (!aEndTag)
		{
		CContentHandlerState* next;
		if (aElement.LocalName().DesC().Compare(KNameTag) == 0)
			{
			next = new(ELeave)CNameState(ContentHandler(), this, iResult);
			}
		else if (aElement.LocalName().DesC().Compare(KValueTag) == 0)
			{
			next = new(ELeave)CValueState(ContentHandler(), this, iResult);
			}
		else
			User::Leave(KErrArgument);
		EnterState(next);
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

void CMemberState::InputL(const TDesC8& /*aBytes*/)
	{
	// Do nothing
	}

void CMemberState::FoundNameL(const TDesC8& aName)
	{
	if (iName.Length())
		User::Leave(KErrArgument); // there can only be one name per member
	
	iName.CreateL(aName);
	}

void CMemberState::FoundValueL(CValue* aValue)
	{
	iValue = aValue;
	}

void CNameState::InputL(const Xml::RTagInfo& aElement, TBool aEndTag)
	{
	if (aElement.LocalName().DesC().Compare(KNameTag) == 0 && aEndTag)
		{
		CMemberState* mem = static_cast<CMemberState*>(iParentState);
		mem->FoundNameL(iBuf);
		iBuf.Close();
		Finish();
		}
	else
		User::Leave(KErrArgument);
	}

void CArrayState::InputL(const RTagInfo& aElement, TBool aEndTag)
	{
	if (aElement.LocalName().DesC().Compare(KArrayTag()) == 0 && aEndTag)
		{
		iParentState->FoundValueL(iValue);
		iValue = NULL; // ownership passed up
		Finish();
		}
	else if (aElement.LocalName().DesC().Compare(KDataTag()) == 0 && !aEndTag)
		{
		CContentHandlerState* next = new(ELeave)CDataState(ContentHandler(), this, iResult);
		EnterState(next);
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

void CArrayState::InputL(const TDesC8& /*aBytes*/)
	{
	}

void CArrayState::FoundValueL(CValue* aValue)
	{
	CArray* array = GetArrayL();
	array->AppendL(aValue);
	}

CArray* CArrayState::GetArrayL()
	{
	if (iValue == NULL)
		iValue = CArray::NewL();
	
	return static_cast<CArray*>(iValue);
	}

void CDataState::InputL(const Xml::RTagInfo& aElement, TBool aEndTag)
	{
	if (aElement.LocalName().DesC().Compare(KDataTag()) == 0 && aEndTag)
		{
		Finish();
		}
	else if (aElement.LocalName().DesC().Compare(KValueTag()) == 0 && !aEndTag)
		{
		CContentHandlerState* next = new(ELeave)CValueState(ContentHandler(), this, iResult);
		EnterState(next);
		}
	else
		{
		User::Leave(KErrArgument);
		}
	
	}

